library flutter_background_service_ohos;

import 'dart:async';
import 'dart:convert';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_service_platform_interface/flutter_background_service_platform_interface.dart';

bool _isMainIsolate = true;

@pragma('vm:entry-point')
Future<void> foregroundOhosEntryPoint(List<String> args) async {
  WidgetsFlutterBinding.ensureInitialized();
  _isMainIsolate = false;

  final service = OhosServiceInstance._();
  final int handle = int.parse(args.first);
  final callbackHandle = CallbackHandle.fromRawHandle(handle);
  final onStart = PluginUtilities.getCallbackFromHandle(callbackHandle);
  if (onStart != null) {
    onStart(service);
  }
}

class FlutterBackgroundServiceOhos extends FlutterBackgroundServicePlatform {
  /// Registers this class as the default instance of [FlutterBackgroundServicePlatform].
  static void registerWith() {
    FlutterBackgroundServicePlatform.instance = FlutterBackgroundServiceOhos();
  }

  FlutterBackgroundServiceOhos._();
  static final FlutterBackgroundServiceOhos _instance =
      FlutterBackgroundServiceOhos._();

  factory FlutterBackgroundServiceOhos() {
    // using flutterEngineGroup create new FlutterEgine will make it
    if (!_isMainIsolate) {
      throw Exception(
        "This class should only be used in the main isolate (UI App)",
      );
    }

    return _instance;
  }

  Future<String> getPlatformVersion() async {
    final result = await _channel.invokeMethod<String>('getPlatformVersion');
    return result ?? 'Unknown';
  }

  Future<void> _handleMethodCall(MethodCall call) async {
    debugPrint(call.method);
    switch (call.method) {
      case "onReceiveData":
        _controller.sink.add(call.arguments);
        break;
      default:
    }
  }

  @override
  Future<bool> start() async {
    final result = await _channel.invokeMethod('start');
    return result ?? false;
  }

  final MethodChannel _channel =
      const MethodChannel('flutter_background_service_ohos_method');

  final EventChannel _eventChannel =
      const EventChannel('flutter_background_service_ohos_event');

  StreamSubscription<dynamic>? _eventChannelListener;
  @override
  Future<bool> configure({
    required IosConfiguration iosConfiguration,
    required AndroidConfiguration androidConfiguration,
    required OhosConfiguration ohosConfiguration,
  }) async {
    _channel.setMethodCallHandler(_handleMethodCall);

    _eventChannelListener?.cancel();
    _eventChannelListener =
        _eventChannel.receiveBroadcastStream().listen((event) {
      _controller.sink.add(event);
    });

    final CallbackHandle? handle =
        PluginUtilities.getCallbackHandle(ohosConfiguration.onStart);

    if (handle == null) {
      throw 'onStart method must be a top-level or static function';
    }

    List<OhosForegroundType>? configForegroundServiceTypes =
        ohosConfiguration.foregroundServiceTypes;
    List<String>? foregroundServiceTypes;
    if (configForegroundServiceTypes != null &&
        configForegroundServiceTypes.isNotEmpty) {
      foregroundServiceTypes = [];
      for (var foregroundServiceType
          in ohosConfiguration.foregroundServiceTypes!) {
        foregroundServiceTypes.add(foregroundServiceType.name);
      }
    }

    final result = await _channel.invokeMethod(
      "configure",
      {
        "background_handle": handle.toRawHandle(),
        "is_foreground_mode": ohosConfiguration.isForegroundMode,
        "auto_start": ohosConfiguration.autoStart,
        "auto_start_on_boot": ohosConfiguration.autoStartOnBoot,
        "initial_notification_content":
            ohosConfiguration.initialNotificationContent,
        "initial_notification_title":
            ohosConfiguration.initialNotificationTitle,
        "notification_channel_id": ohosConfiguration.notificationChannelId,
        "foreground_notification_id":
            ohosConfiguration.foregroundServiceNotificationId,
        "foreground_service_types": foregroundServiceTypes,
      },
    );

    return result ?? false;
  }

  @override
  Future<bool> isServiceRunning() async {
    var result = await _channel.invokeMethod("isServiceRunning");
    return result ?? false;
  }

  final _controller = StreamController.broadcast(sync: true);

  void dispose() {
    _controller.close();
  }

  @override
  void invoke(String method, [Map<String, dynamic>? args]) {
    _channel.invokeMethod("sendData", {
      'method': method,
      'args': jsonEncode(args),
    });
  }

  @override
  Stream<Map<String, dynamic>?> on(String method) {
    return _controller.stream.transform(
      StreamTransformer.fromHandlers(
        handleData: (data, sink) {
          final result = jsonDecode(data);
          if (result['method'] == method) {
            sink.add(result['args']);
          }
        },
      ),
    );
  }
}

class OhosServiceInstance extends ServiceInstance {
  static const MethodChannel _channel =
      MethodChannel('flutter_background_service_ohos_bg');

  OhosServiceInstance._() {
    _channel.setMethodCallHandler(_handleMethodCall);
  }

  final _controller = StreamController.broadcast(sync: true);
  Future<void> _handleMethodCall(MethodCall call) async {
    switch (call.method) {
      case "onReceiveData":
        _controller.sink.add(call.arguments);
        break;
      default:
    }
  }

  @override
  void invoke(String method, [Map<String, dynamic>? args]) {
    _channel.invokeMethod('sendData', {
      'method': method,
      'args': jsonEncode(args),
    });
  }

  @override
  Future<void> stopSelf() async {
    await _channel.invokeMethod("stopService");
  }

  @override
  Stream<Map<String, dynamic>?> on(String method) {
    return _controller.stream.transform(
      StreamTransformer.fromHandlers(
        handleData: (data, sink) {
          if (data['method'] == method) {
            sink.add(data['args']);
          }
        },
      ),
    );
  }

  Future<void> setForegroundNotificationInfo({
    required String title,
    required String content,
  }) async {
    await _channel.invokeMethod("setNotificationInfo", {
      "title": title,
      "content": content,
    });
  }

  Future<void> setAsForegroundService() async {
    await _channel.invokeMethod("setForegroundMode", {
      'value': true,
    });
  }

  Future<void> setAsBackgroundService() async {
    await _channel.invokeMethod("setForegroundMode", {
      'value': false,
    });
  }

  /// returns true when the current Service instance is in foreground mode.
  Future<bool> isForegroundService() async {
    final result = await _channel.invokeMethod<bool>('isForegroundMode');
    return result ?? false;
  }

  Future<void> setAutoStartOnBootMode(bool value) async {
    await _channel.invokeMethod("setAutoStartOnBootMode", {
      "value": value,
    });
  }

  Future<bool> openApp() async {
    final result = await _channel.invokeMethod('openApp');
    return result ?? false;
  }
}
